package com.util.dao;

import com.util.JDBCUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @ClassName BaseDao
 * @Description 数据库操作的基础类，提供基本的查询、更新方法
 * @Author Kx
 * @Date 2025/3/31 8:29
 * @Version 1.0
 */
public class BaseDao {
    /***
     * @author: kx
     * @date: 2025/3/31 10:34
     * @param clazz : 要映射的JAVA对象类
     * @param sql : SQL查询语句
     * @param params : SQL查询语句中的参数
     * @return T : 返回的对象类型
     * @description: 查询单条数据
    */
    public <T> T baseQuery(Class<T> clazz, String sql, Object... params) {
        // 参数校验
        Objects.requireNonNull(clazz,"目标类型不能为null");
        Objects.requireNonNull(sql,"SQL语句不能为null");

        // 获取数据库连接和获取预编译对象
        try (Connection connection = JDBCUtil.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            // 设置SQL语句中占位符的值
            setParameters(preparedStatement, params);
            // 执行查询并存储到结果集中
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                // 判断结果集下一行是否有数据,有的话就将结果集映射为对象并返回
                return resultSet.next() ? mapRow(resultSet, clazz) : null;
            }
        } catch (SQLException | ReflectiveOperationException e) {
            // 处理异常
            throw new RuntimeException("执行SQL查询时发生错误", e);
        }
    }
    /***
     * @author: kx
     * @date: 2025/3/31 11:01
     * @param clazz : 要映射的JAVA对象类
     * @param sql : SQL查询语句
     * @param params : SQL查询语句中的参数
     * @return java.util.List<T> : 返回对象集合
     * @description: 查询多条数据
    */
    public <T> List<T> baseQueryList(Class<T> clazz, String sql, Object... params) {
        // 参数校验
        Objects.requireNonNull(clazz,"目标类型不能为null");
        Objects.requireNonNull(sql,"SQL语句不能为null");

        // 获取数据库连接和获取预编译对象
        try (Connection connection = JDBCUtil.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            // 设置SQL语句中占位符的值
            setParameters(preparedStatement, params);
            // 执行查询并存储到结果集中
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                // 创建集合
                List<T> list = new ArrayList<>();
                // 循环遍历结果集
                while (resultSet.next()) {
                    // 将结果集映射为对象
                    T t = mapRow(resultSet, clazz);

                    if (t != null){
                        // 将对象添加到集合中
                        list.add(t);
                    }
                }
                return list;
            }
        } catch (SQLException | ReflectiveOperationException e){
            throw new RuntimeException("执行SQL查询时发生错误", e);
        }
    }

    /***
     * @author: kx
     * @date: 2025/3/31 11:27
     * @param sql : SQL更新语句
     * @param params : SQL语句中的参数
     * @return int : 更新操作影响的行数
     * @description: 执行数据库更新操作的基础方法
    */
    public int baseUpdate(String sql, Object... params) {
        // 参数校验
        Objects.requireNonNull(sql, "SQL语句不能为null");

        // 获取数据库连接和获取预编译对象
        try (Connection connection = JDBCUtil.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            // 设置SQL语句中占位符的值
            setParameters(preparedStatement, params);

            // 执行更新并返回受影响的行数
            return preparedStatement.executeUpdate();
        } catch (SQLException e){
            // 处理异常
            throw new RuntimeException("执行SQL更新时发生错误", e);
        }
    }

    /***
     * @author: kx
     * @date: 2025/3/31 15:04
     * @param clazz : 要映射的JAVA对象类
     * @param sql : SQL查询语句
     * @param fieldName : 需要查询的字段名
     * @param params : SQL语句中的参数
     * @return java.lang.Object : 返回的值
     * @description: 查询单个属性值
    */
    public <T> Object querySingleFieldValue(Class<?> clazz, String sql, String fieldName, Object... params) {
        // 参数校验
        Objects.requireNonNull(clazz, "目标类型不能为null");
        Objects.requireNonNull(sql, "SQL语句不能为null");
        Objects.requireNonNull(fieldName, "字段名不能为null");

        // 获取数据库连接
        // 获取数据库连接和获取预编译对象
        try (Connection connection = JDBCUtil.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            // 设置SQL语句中占位符的值
            setParameters(preparedStatement, params);

            // 执行查询并存储到结果集中
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                // 循环遍历结果集
                if (resultSet.next()) {
                    // 获取指定字段的值
                    return resultSet.getObject(fieldName);
                }
                return null;
            }
        } catch (SQLException e){
            throw new RuntimeException("字段不存在" + fieldName, e);
        }
    }

    /***
     * @author: kx
     * @date: 2025/3/31 8:38
     * @param parameters : 预编译对象
     * @param params : 参数
     * @description: 设置预编译参数
    */
    private void setParameters(PreparedStatement parameters, Object... params) throws SQLException {
        for (int i = 0; i < params.length; i++) {
            parameters.setObject(i+1,params[i]);
        }
    }

    /***
     * @author: kx
     * @date: 2025/3/31 9:33
     * @param resultSet : 结果集
     * @param clazz : 映射对象
     * @return T : 返回对象
     * @description: 将查询结果集映射为对象
     */
    private <T> T mapRow(ResultSet resultSet, Class<T> clazz) throws SQLException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 判断结果集是否为空
        if (resultSet == null) {
            return null;
        }

        // 获取实体类对象
        T t = clazz.getDeclaredConstructor().newInstance();

        // 获取结果集元数据
        ResultSetMetaData metaData = resultSet.getMetaData();

        // 获取列的数量
        int columnCount = metaData.getColumnCount();

        // 循环遍历列
        for (int i = 1; i <= columnCount; i++){
            // 获取列名
            String columnLabel = metaData.getColumnLabel(i);

            try {
                // 获取字段名
                Field declaredField = clazz.getDeclaredField(columnLabel);

                // 设置字段可访问
                declaredField.setAccessible(true);

                // 获取列值
                Object object = resultSet.getObject(columnLabel);

                // 如果是LocalDateTime类型，则转换为Timestamp类型
                declaredField.set(t,convertValue(declaredField.getType(),object));
            } catch (NoSuchFieldException e){
                // 如果没有字段，则跳过
                continue;
            }
        }
        return t;
    }

    /***
     * @author: kx
     * @date: 2025/3/31 10:50
     * @param fieldType : 字段类型
     * @param value : 需要转换的原始值
     * @return java.lang.Object : 转换后的值
     * @description: 将原始值转换为指定类型的值
     * 用于不同数据之间进行转换,特别是在处理数据库读写操作时
    */
    private <T> T convertValue(Class<?> fieldType, Object value) {
        // 如果值为null，则返回默认值
        if (value == null) {
            return (T) getDefaultValue(fieldType);
        }

        try {
            // 处理常见类型转换
            if (fieldType == String.class) {
                // 将值转换为字符串
                return (T) value.toString();
            } else if (fieldType == Integer.class || fieldType == int.class) {
                // 将值转换为整数
                return (T) Integer.valueOf(value.toString());
            } else if (fieldType == Long.class || fieldType == long.class) {
                // 将值转换为长整数
                return (T) Long.valueOf(value.toString());
            } else if (fieldType == Boolean.class || fieldType == boolean.class) {
                // 将值转换为布尔值
                return (T) Boolean.valueOf(value.toString());
            } else if (fieldType == LocalDateTime.class) {
                if (value instanceof Timestamp) {
                    // 将值转换为LocalDateTime
                    return (T) ((Timestamp) value).toLocalDateTime();
                }
                // 将值转换为LocalDateTime
                return (T) LocalDateTime.parse(value.toString());
            } else if (fieldType == Timestamp.class) {
                if (value instanceof LocalDateTime) {
                    // 将值转换为Timestamp
                    return (T) Timestamp.valueOf((LocalDateTime) value);
                }
                // 将值转换为Timestamp
                return (T) Timestamp.valueOf(value.toString());
            } else if (fieldType == Byte.class || fieldType == byte.class) {
                // 将值转换为Byte
                return (T) Byte.valueOf(value.toString());
            } else if (fieldType == float.class || fieldType == Float.class) {
                // 将值转换为Float
                return (T) Float.valueOf(value.toString());
            } else if (fieldType == double.class || fieldType == Double.class) {
                // 将值转换为Double
                return (T) Double.valueOf(value.toString());
            }
            // 其他类型转换...

            // 如果类型匹配直接返回
            if (fieldType.isInstance(value)) {
                return (T) value;
            }
            // 如果没有匹配类型，抛出异常
            throw new RuntimeException("不支持的类型转换: " + value.getClass() + " -> " + fieldType);
        } catch (Exception e) {
            throw new RuntimeException("类型转换失败", e);
        }
    }



    /***
     * @author: kx
     * @date: 2025/3/31 9:51
     * @param type : 设置值的类型
     * @return java.lang.Object : 默认值
     * @description: 根据设置值的类型获取相对应的默认值
    */
    private static Object getDefaultValue(Class<?> type) {
        if(type == int.class || type == Integer.class){
            return 0;
        } else if (type == short.class || type == Short.class) {
            return 0;
        } else if (type == byte.class || type == Byte.class) {
            return 0;
        } else if (type == long.class || type == Long.class) {
            return 0L;
        } else if (type == float.class || type == Float.class) {
            return 0.0f;
        } else if (type == double.class || type == Double.class) {
            return 0.0;
        } else if (type == boolean.class || type == Boolean.class) {
            return false;
        } else if (type == char.class || type == Character.class) {
            return '\u0000';
        } else {
            return null;
        }
    }
}
